﻿using WebApp.Common.Caching;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;

namespace WebApp.Web.Core.Auth
{
    /// <summary>
    /// 检查Action是否标记IgnoreAuthorize|AuthorizeFilterAttribute
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
    public class PathAuthorizeFilterAttribute : ActionFilterAttribute
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger("WebApp.Log");

        /// <summary>
        /// {0}:请求路径
        /// </summary>
        private const string PATH_AUTHORIZE_KEY = "WebApp.PathAuthorize.All.{0}";
        /// <summary>
        /// 缓存KEY（用于清除缓存）
        /// </summary>
        public const string PATH_AUTHORIZE_ALLKEY = "WebApp.PathAuthorize.All.";

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }
            if (filterContext.HttpContext == null)
            {
                throw new ArgumentNullException("filterContext.HttpContext");
            }

            string path = filterContext.HttpContext.Request.Path.ToLower();

            //解决站点发布到子路径的情况
            if (!"/".Equals(filterContext.HttpContext.Request.ApplicationPath))
            {
                path = path.Replace(filterContext.HttpContext.Request.ApplicationPath + "/", "/");
            }

            log.Info(UrlHelper.GenerateContentUrl("~/Error/Error?msg=xxx", filterContext.HttpContext));

            //log.Info("PathAuthorizeFilterAttribute:" + path);
            //log.Info("Url:" + filterContext.HttpContext.Request.Url);
            //log.Info("RawUrl:" + filterContext.HttpContext.Request.RawUrl);
            //log.Info("PhysicalPath:" + filterContext.HttpContext.Request.PhysicalPath);
            //log.Info("PhysicalApplicationPath:" + filterContext.HttpContext.Request.PhysicalApplicationPath);
            //log.Info("ApplicationPath:" + filterContext.HttpContext.Request.ApplicationPath);

            string key = string.Format(PATH_AUTHORIZE_KEY, path);
            bool validpath = new MemoryCacheManager().Get<bool>(key, () =>
            {
                if (IsIgnoreUrl(path)) return true;

                object[] customAttrs = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AuthorizeFilterAttribute), false);
                if (customAttrs != null && customAttrs.Length > 0) return true;

                customAttrs = filterContext.ActionDescriptor.GetCustomAttributes(typeof(IgnoreAuthorizeFilterAttribute), false);
                if (customAttrs != null && customAttrs.Length > 0) return true;

                return false;
            });
            if (!validpath)
            {
                filterContext.Result = new HttpUnauthorizedResult();
            }
        }

        /// <summary>
        /// 忽略路径
        /// </summary>
        /// <param name="httpcontext"></param>
        /// <returns></returns>
        private bool IsIgnoreUrl(string path)
        {
            if (path.Contains("/error/") || path.Contains("/eval/"))
            {
                return true;
            }

            string[] ignoreUrls = new[] {
                "/"
                ,"/home/index"
                ,"/admin"
                ,"/admin/index"
                ,"/member/login"
                ,"/member/relogin"
                ,"/member/logincheck"
                ,"/member/checkvalidatecode"
                ,"/member/findpassword"
                ,"/member/validateunamecode"
                ,"/member/validateusername"
                ,"/member/validatetel"
                ,"/member/sendvaildatecode"
                ,"/member/getvalidatecode"
                ,"/member/mobilevalidatecode"
                ,"/member/logout"
                ,"/member/errornumber"
                ,"/member/sendvaildatecode"
                ,"/member/validatecode"
                ,"/member/updatepwd"
                ,"/member/pwdlengthchk"
                ,"/member/forgotpwdend"
            };

            return ignoreUrls.Contains(path);
        }
    }
}
